package cn.labbook.bluetooth.controller.PayController;

import cn.labbook.bluetooth.constant.AmmeterConstant;
import cn.labbook.bluetooth.constant.PayConstant;
import cn.labbook.bluetooth.constant.TenantPayConstant;
import cn.labbook.bluetooth.entity.*;
import cn.labbook.bluetooth.entity.wxpay.WXPay;
import cn.labbook.bluetooth.entity.wxpay.WXPayment;
import cn.labbook.bluetooth.service.*;
import cn.labbook.bluetooth.util.weixin.pay.PayUtil;
import cn.labbook.bluetooth.util.weixin.pay.UUIDHexGenerator;
import cn.labbook.bluetooth.util.weixin.pay.XmlUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@Slf4j
public class WeiXinPaymentController {
    @Autowired
    ITenantPayService tenantPayService;
    @Autowired
    IAmmeterService ammeterService;
    @Autowired
    IServicePayService servicePayService;
    @Autowired
    IUserService userService;
    @Autowired
    IAdminService adminService;
    @Autowired
    IConstantService constantService;
    @Autowired
    private IServicePayCreateService servicePayCreateService;
    @Autowired
    private ITransferRecordService transferRecordService;


    private final String mch_id = "1491927112";// 商户号
    private final String spbill_create_ip = "127.0.0.1";// 终端IP
    private final String notify_url = "https://bluetooth.zoercn.com/bluetooth/weixin/paycallback.do";// 通知地址
    private final String trade_type = "JSAPI";// 交易类型
    private final String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    private final String key = "&key=zk123sqweqdsadZK9120392018490184"; // 商户支付密钥
    private final String appid = "wxafeeae8c758d838d";


    @RequestMapping("/weixin/paymenttest.do")
    @ResponseBody
    public Object paymenttest(
            @RequestParam(required = true) String ammeterId,
            @RequestParam(required = true) String total_fee
    ) {
        String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        String code = PayUtil.createCode(8);
        String out_trade_no = mch_id + today + code;// 商户订单号
        //------------------------------------------
        TenantPay tenantPay = new TenantPay();
        tenantPay.setTenantPayId(out_trade_no);
        tenantPay.setAmmeterId(ammeterId);

        LambdaQueryWrapper<Ammeter> lambda = new LambdaQueryWrapper<>();
        lambda.eq(Ammeter::getAmmeterId, ammeterId);
        WXPayment paymentPo = new WXPayment();

        double rate = this.getNowPrice(ammeterId);
        double totalFeeValue = Double.parseDouble(total_fee);
        double amount = totalFeeValue / 100;

        BigDecimal wxFee = (new BigDecimal(totalFeeValue * 0.006D)).setScale(0, RoundingMode.HALF_UP);

        Double wxPlatformAmount = Double.parseDouble(wxFee.toString());
        tenantPay.setWxPlatformAmount(wxPlatformAmount / 100.0D);
        double totalPlatformAmount = totalFeeValue * rate / 100;
        double zkisPlatformAmount = (totalPlatformAmount - wxPlatformAmount) / 100;
        if (ammeterService.getOne(lambda).getIsSkimp() == AmmeterConstant.IsSkimp.YES) {
            log.info("房客承担服务费===========》");

            double payAmount = totalFeeValue + totalPlatformAmount;

            paymentPo.setTotal_fee(String.valueOf(payAmount));
            tenantPay.setPayAmouont(payAmount / 100);
            tenantPay.setPayTrueAmouont(amount);
            tenantPay.setManagerAmount(amount);
        } else {
            log.info("房东承担服务费===========》");

            tenantPay.setPayAmouont(amount);
            paymentPo.setTotal_fee(total_fee);
            tenantPay.setPayTrueAmouont(amount);

            Double managerAmount = totalFeeValue - totalPlatformAmount;
            tenantPay.setManagerAmount(managerAmount / 100);
        }
        tenantPay.setZkisPlatformAmount(zkisPlatformAmount);
        tenantPay.setPayTime(new Date());
        tenantPay.setIsArrive(1);
        tenantPay.setPayStatus(1);

        if (amount > TenantPayConstant.MAX_AMOUNT) {
            tenantPay.setRemark("人工转账");
        } else {
            tenantPay.setRemark("次日到账");
        }

        tenantPayService.save(tenantPay);
        return tenantPay;
    }


    // 随机字符串 签名 订单号 openid 校验用户姓名选项

    /**
     * @param openId
     * @param total_fee 订单总金额，单位为分。
     * @param body      商品简单描述，该字段请按照规范传递。 例：腾讯充值中心-心悦会员充值
     * @param attach    附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用。 例：广州分店
     * @return
     */
    @RequestMapping("/weixin/payment.do")
    @ResponseBody
    public JSONObject payment(
            @RequestParam(required = true) String ammeterId, @RequestParam(required = true) String openId,
            @RequestParam(required = true) String total_fee, @RequestParam(required = false) String body,
            @RequestParam(required = false) String attach
    ) throws UnsupportedEncodingException, DocumentException {
        JSONObject JsonObject = new JSONObject();
        body = new String(body.getBytes("UTF-8"), "ISO-8859-1");
        String nonce_str = UUIDHexGenerator.generate();// 随机字符串
        String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        String code = PayUtil.createCode(8);
        String out_trade_no = mch_id + today + code;// 商户订单号
        //------------------------------------------
        TenantPay tenantPay = new TenantPay();
        tenantPay.setTenantPayId(out_trade_no);
        tenantPay.setAmmeterId(ammeterId);
        tenantPay.setOpenId(openId);

        LambdaQueryWrapper<Ammeter> lambda = new LambdaQueryWrapper<>();
        lambda.eq(Ammeter::getAmmeterId, ammeterId);
        WXPayment paymentPo = new WXPayment();

        double rate = this.getNowPrice(ammeterId);
        double totalFeeValue = Double.parseDouble(total_fee);
        double amount = totalFeeValue / 100;

        BigDecimal wxFee = (new BigDecimal(totalFeeValue * 0.006D)).setScale(0, RoundingMode.HALF_UP);

//        Double wxPlatformAmount = Double.parseDouble(wxFee.toString());
//        tenantPay.setWxPlatformAmount(wxPlatformAmount / 100.0D);
//        double totalPlatformAmount = totalFeeValue * rate / 100;
//        double zkisPlatformAmount = (totalPlatformAmount - wxPlatformAmount) / 100;
//        if (ammeterService.getOne(lambda).getIsSkimp() == AmmeterConstant.IsSkimp.YES) {
//            log.info("房客承担服务费===========》");
//
//            double payAmount = totalFeeValue + totalPlatformAmount;
//
//            paymentPo.setTotal_fee(String.valueOf(payAmount));
//            tenantPay.setPayAmouont(payAmount / 100);
//            tenantPay.setPayTrueAmouont(amount);
//            tenantPay.setManagerAmount(amount);
//        } else {
//            log.info("房东承担服务费===========》");
//
//            tenantPay.setPayAmouont(amount);
//            paymentPo.setTotal_fee(total_fee);
//            tenantPay.setPayTrueAmouont(amount);
//
//            Double managerAmount = totalFeeValue - totalPlatformAmount;
//            tenantPay.setManagerAmount(managerAmount / 100);
//        }
//        tenantPay.setZkisPlatformAmount(zkisPlatformAmount);

        tenantPay.setWxPlatformAmount(Double.parseDouble(wxFee.toString()) / 100.0D);

        if(ammeterService.getOne(lambda).getIsSkimp()==AmmeterConstant.IsSkimp.YES) {
            double realAmount = amount * (100 + rate) / 100;
            BigDecimal bigDecimal = new BigDecimal(realAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
            tenantPay.setPayAmouont(bigDecimal.doubleValue());
            log.info("房客承担服务费===========》"+ bigDecimal);
            paymentPo.setTotal_fee((new BigDecimal(realAmount*100).setScale(0, BigDecimal.ROUND_HALF_UP).toString()));


            double total_fee3 = totalFeeValue/(100+rate)*100;
            double v = (new BigDecimal(total_fee3)).setScale(0, 4).doubleValue();
            tenantPay.setPayTrueAmouont(amount);
            tenantPay.setManagerAmount(amount);
            log.info("=====================================房客承担total_fee："+totalFeeValue);
            log.info("=====================================房客承担v："+v);
            log.info("=====================================房客承担wxplatform："+Double.parseDouble(wxFee.toString()));
            double total_fee1 = (totalFeeValue - v - Double.parseDouble(wxFee.toString())) / 100.0D;
            tenantPay.setZkisPlatformAmount((new BigDecimal(total_fee1)).setScale(2, 4).doubleValue());
        } else {
            log.info("房东承担服务费===========》");
            tenantPay.setPayAmouont(amount);
            paymentPo.setTotal_fee(total_fee);
            tenantPay.setPayTrueAmouont(amount);

            double total_fee1 = totalFeeValue * (100-rate) / 100;
            BigDecimal bigDecimal = new BigDecimal(total_fee1).setScale(0, BigDecimal.ROUND_HALF_UP);
            tenantPay.setManagerAmount(Double.parseDouble(bigDecimal.toString()) / 100.0D);

            double total_fee3 = totalFeeValue * (rate-0.6) / 100;
            BigDecimal bigDecimal1 = new BigDecimal(total_fee3).setScale(0, BigDecimal.ROUND_HALF_UP);
            tenantPay.setZkisPlatformAmount((Double.parseDouble(bigDecimal1.toString()))/100);
        }
        tenantPay.setPayTime(new Date());
        tenantPay.setIsArrive(1);
        tenantPay.setPayStatus(1);

        if (amount > TenantPayConstant.MAX_AMOUNT) {
            tenantPay.setRemark("人工转账");
        } else {
            tenantPay.setRemark("次日到账");
        }

        tenantPayService.save(tenantPay);

        //------------------------------------------
        String openid = openId;// 用户标识
        paymentPo.setAppid(appid);
        paymentPo.setMch_id(mch_id);
        paymentPo.setNonce_str(nonce_str);
        String newbody = new String(body.getBytes("ISO-8859-1"), "UTF-8");// 以utf-8编码放入paymentPo，微信支付要求字符编码统一采用UTF-8字符编码
        paymentPo.setBody(newbody);
        paymentPo.setOut_trade_no(out_trade_no);
        paymentPo.setSpbill_create_ip(spbill_create_ip);
        paymentPo.setNotify_url(notify_url);
        paymentPo.setTrade_type(trade_type);
        paymentPo.setOpenid(openid);
        // 把请求参数打包成数组
        Map<String, Object> sParaTemp = new HashMap<>();
        sParaTemp.put("appid", paymentPo.getAppid());
        sParaTemp.put("mch_id", paymentPo.getMch_id());
        sParaTemp.put("nonce_str", paymentPo.getNonce_str());
        sParaTemp.put("body", paymentPo.getBody());
        sParaTemp.put("out_trade_no", paymentPo.getOut_trade_no());
        sParaTemp.put("total_fee", paymentPo.getTotal_fee());
        sParaTemp.put("spbill_create_ip", paymentPo.getSpbill_create_ip());
        sParaTemp.put("notify_url", paymentPo.getNotify_url());
        sParaTemp.put("trade_type", paymentPo.getTrade_type());
        sParaTemp.put("openid", paymentPo.getOpenid());
        // 除去数组中的空值和签名参数
        Map sPara = PayUtil.paraFilter(sParaTemp);
        String prestr = PayUtil.createLinkString(sPara); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        // MD5运算生成签名
        String mysign = PayUtil.sign(prestr, key, "utf-8").toUpperCase();
        paymentPo.setSign(mysign);
        // 打包要发送的xml
        String respXml = XmlUtil.messageToXML(paymentPo);
        // 打印respXml发现，得到的xml中有“__”不对，应该替换成“_”
        respXml = respXml.replace("__", "_");
        String param = respXml;
        // String result = SendRequestForUrl.sendRequest(url, param);//发起请求
        String result = PayUtil.httpRequest(url, "POST", param);
        // 将解析结果存储在Map中
        Map map = new HashMap();
        InputStream in = new ByteArrayInputStream(result.getBytes());
        // 读取输入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(in);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子节点
        List<Element> elementList = root.elements();
        for (Element element : elementList) {
            map.put(element.getName(), element.getText());
        }
        // 返回信息
        String return_code = map.get("return_code").toString();// 返回状态码
        String return_msg = map.get("return_msg").toString();// 返回信息
        String result_code = map.get("result_code").toString();// 返回状态码

        if ("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)) {
            // 业务结果
            String prepay_id = map.get("prepay_id").toString();// 返回的预付单信息
            String nonceStr = UUIDHexGenerator.generate();
            JsonObject.put("nonceStr", nonceStr);
            JsonObject.put("package", "prepay_id=" + prepay_id);
            Long timeStamp = System.currentTimeMillis() / 1000;
            JsonObject.put("timeStamp", timeStamp + "");
            String stringSignTemp = "appId=" + appid + "&nonceStr=" + nonceStr + "&package=prepay_id=" + prepay_id + "&signType=MD5&timeStamp=" + timeStamp;
            // 再次签名
            String paySign = PayUtil.sign(stringSignTemp, key, "utf-8").toUpperCase();
            JsonObject.put("paySign", paySign);
        }
        return JsonObject;
    }


    /**
     * 预支付时填写的 notify_url ，支付成功后的回调接口
     *
     * @param request
     */
    @RequestMapping("/weixin/paycallback.do")
    @ResponseBody
    public String paycallback(HttpServletRequest request) {
        try {
            log.info("进入手机扫码支付回调");
            Map<String, Object> dataMap = XmlUtil.parseXML(request);
            log.info(dataMap.toString());
            TenantPay tenantPay = tenantPayService.getById(dataMap.get("out_trade_no").toString());
            if (tenantPay != null) {
                log.info("进入小程序充值电费回调");
                if (dataMap.get("return_code").equals("SUCCESS")) {
                    log.info("回调返回成功");
                    tenantPay.setPayStatus(PayConstant.YES);
                    tenantPayService.updateById(tenantPay);
//                    this.pay(dataMap,tenantPay.getAmmeterId(),tenantPay.getTenantPayId());
                    if (tenantPayService.getIntercept(tenantPay.getAmmeterId()) == 1) {
                        log.info("该房东被拦截，将停止转账流程");
                        return null;
                    }
                    if (constantService.inTimingTransferOpen()) {
                        transferRecordService.transfer(tenantPay);
                    }
                } else if (dataMap.get("return_code").equals("FAIL")) {
                    log.info("回调返回失败");
                    tenantPay.setPayStatus(PayConstant.NO);
                    tenantPayService.updateById(tenantPay);
                }
                log.info("支付成功");
                this.pay(tenantPay.getAmmeterId(), Double.parseDouble(dataMap.get("total_fee").toString()));
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "<xml>\n" +
                "  <return_code><![CDATA[SUCCESS]]></return_code>\n" +
                "  <return_msg><![CDATA[OK]]></return_msg>\n" +
                "</xml>";
    }

    public void pay(String ammeterId, Double price) {
        Ammeter ammeter = ammeterService.getById(ammeterId);
        if (ammeter.getIsSkimp() == 1) {
            ammeter.setBalance(ammeter.getBalance() + (price - (price * 0.006)));
        } else {
            ammeter.setBalance(ammeter.getBalance() + price);
        }
        if (ammeter.getServerStatus() == AmmeterConstant.serverStatus.EXPIRE) {
            ammeter.setServerStatus(AmmeterConstant.serverStatus.NOTEXPIRE);
        }
        ammeterService.updateById(ammeter);
    }

    // 房东小程序充值服务费（作废）
    @RequestMapping("/weixin/serverPay")
    @ResponseBody
    public JSONObject serverPay(@RequestParam(required = true) String ammeterIds, @RequestParam(required = true) String openId, @RequestParam(required = false) String body) throws UnsupportedEncodingException, DocumentException {
        JSONObject JsonObject = new JSONObject();
        body = new String(body.getBytes("UTF-8"), "ISO-8859-1");
        String nonce_str = UUIDHexGenerator.generate();// 随机字符串
        String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        String code = PayUtil.createCode(8);
        String out_trade_no = mch_id + today + code;// 商户订单号

        String[] split = ammeterIds.split(",");
        Double totalfee = 0.00;
        double aa = 0.00;
        LambdaQueryWrapper<User> lambda = new LambdaQueryWrapper<>();
        lambda.eq(User::getOpenId, openId);
        User user = userService.getOne(lambda);
        Admin admin = adminService.getById(user.getAdminId());
        for (String ammeterId : split) {
            Ammeter ammeter = ammeterService.getById(ammeterId);
            boolean flag = (admin.getPermission() == 1);
            Constant serverPrice = constantService.getById("serverPrice");
            ServicePayCreate servicePayCreate = new ServicePayCreate();
            servicePayCreate.setAmmeterId(ammeterId);
            servicePayCreate.setCreateTime(new Date());
            servicePayCreate.setPayType(2);
            servicePayCreate.setServicePayId(out_trade_no);
            if (ammeter.getPayPrice() == 0 || !flag) {
                servicePayCreate.setPayPrice(serverPrice.getV());
            } else {
                if (serverPrice.getV() > ammeter.getPayPrice()) {
                    servicePayCreate.setPayPrice(serverPrice.getV());
                } else {
                    servicePayCreate.setPayPrice(ammeter.getPayPrice());
                }
            }
            totalfee += servicePayCreate.getPayPrice();
            BigDecimal bigDecimal = new BigDecimal(Double.toString(totalfee));
            aa = Double.parseDouble(bigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP).toString());
            servicePayCreateService.save(servicePayCreate);
        }

        ServicePay servicePay = new ServicePay();
        servicePay.setServicePayId(out_trade_no);
        servicePay.setCreateTime(new Date());
        servicePay.setPayAmount(aa);
        servicePay.setPayStatus(PayConstant.NO);
        servicePayService.save(servicePay);


        //------------------------------------------
        String openid = openId;// 用户标识
        WXPayment paymentPo = new WXPayment();
        paymentPo.setAppid(appid);
        paymentPo.setMch_id(mch_id);
        paymentPo.setNonce_str(nonce_str);
        String newbody = new String(body.getBytes("ISO-8859-1"), "UTF-8");// 以utf-8编码放入paymentPo，微信支付要求字符编码统一采用UTF-8字符编码
        paymentPo.setBody(newbody);
        paymentPo.setOut_trade_no(out_trade_no);
        paymentPo.setTotal_fee(String.valueOf((int) (aa * 100)));
        paymentPo.setSpbill_create_ip(spbill_create_ip);
        paymentPo.setNotify_url(notify_url);
        paymentPo.setTrade_type(trade_type);
        paymentPo.setOpenid(openid);
        // 把请求参数打包成数组
        Map<String, Object> sParaTemp = new HashMap();
        sParaTemp.put("appid", paymentPo.getAppid());
        sParaTemp.put("mch_id", paymentPo.getMch_id());
        sParaTemp.put("nonce_str", paymentPo.getNonce_str());
        sParaTemp.put("body", paymentPo.getBody());
        sParaTemp.put("out_trade_no", paymentPo.getOut_trade_no());
        sParaTemp.put("total_fee", paymentPo.getTotal_fee());
        sParaTemp.put("spbill_create_ip", paymentPo.getSpbill_create_ip());
        sParaTemp.put("notify_url", paymentPo.getNotify_url());
        sParaTemp.put("trade_type", paymentPo.getTrade_type());
        sParaTemp.put("openid", paymentPo.getOpenid());
        // 除去数组中的空值和签名参数
        Map sPara = PayUtil.paraFilter(sParaTemp);
        String prestr = PayUtil.createLinkString(sPara); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        // MD5运算生成签名
        String mysign = PayUtil.sign(prestr, key, "utf-8").toUpperCase();
        paymentPo.setSign(mysign);
        // 打包要发送的xml
        String respXml = XmlUtil.messageToXML(paymentPo);
        // 打印respXml发现，得到的xml中有“__”不对，应该替换成“_”
        respXml = respXml.replace("__", "_");
        String param = respXml;
        // String result = SendRequestForUrl.sendRequest(url, param);//发起请求
        String result = PayUtil.httpRequest(url, "POST", param);
        // 将解析结果存储在Map中
        Map map = new HashMap();
        InputStream in = new ByteArrayInputStream(result.getBytes());
        // 读取输入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(in);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子节点
        List<Element> elementList = root.elements();
        for (Element element : elementList) {
            map.put(element.getName(), element.getText());
        }
        // 返回信息
        System.out.println(map.toString());
        String return_code = map.get("return_code").toString();// 返回状态码
        String result_code = map.get("result_code").toString();// 返回状态码

        if ("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)) {
            // 业务结果
            String prepay_id = map.get("prepay_id").toString();// 返回的预付单信息
            String nonceStr = UUIDHexGenerator.generate();
            JsonObject.put("nonceStr", nonceStr);
            JsonObject.put("package", "prepay_id=" + prepay_id);
            Long timeStamp = System.currentTimeMillis() / 1000;
            JsonObject.put("timeStamp", timeStamp + "");
            String stringSignTemp = "appId=" + appid + "&nonceStr=" + nonceStr + "&package=prepay_id=" + prepay_id + "&signType=MD5&timeStamp=" + timeStamp;
            // 再次签名
            String paySign = PayUtil.sign(stringSignTemp, key, "utf-8").toUpperCase();
            JsonObject.put("paySign", paySign);
        }
        return JsonObject;
    }

    // 获取费率
    public double getNowPrice(String ammeterId) {
        Double price;

        LambdaQueryWrapper<Ammeter> lambda1 = new QueryWrapper<Ammeter>().lambda();
        lambda1.eq(Ammeter::getAmmeterId, ammeterId);
        Ammeter one = ammeterService.getOne(lambda1);

        LambdaQueryWrapper<Admin> lambda2 = new QueryWrapper<Admin>().lambda();
        lambda2.eq(Admin::getAdminId, one.getAdminId());
        Admin one1 = adminService.getOne(lambda2);
        price = one1.getPayRate();
        return price;
    }
}

